// This is the main DLL file.

#include "pch.h"

#include "ComputerEngine.h"
#include "..\EngineManager\ThreadManagement.h"

using namespace DbReader;
using namespace Platform;
using namespace Windows::System::Threading;
using namespace Windows::Storage;

namespace CXEngineWrapper
{
	Engine::Engine()
	{
		 _openingFile = ref new String(L"opening.txt");
		InitEngine();
		InitEvents();
	}

	bool Engine::LoadOpeningDatabase(String^ sOpeningPath)
	{
		String^ sFilePath =	sOpeningPath;
		if(sFilePath->Data()[sFilePath->Length() - 1] != '\\')
			sFilePath += "\\";
		sFilePath = sFilePath + _openingFile;

		std::wstring* stdwFile = new std::wstring(sFilePath->Data());
		setlocale(LC_CTYPE, "");
		std::string stdFile(stdwFile->begin(), stdwFile->end());

		bool bResult = InitOpeningBook(stdFile);

		delete stdwFile;

		return bResult;
	}

	void Engine::PreloadEndgameDatabase()
	{
		::SetEvent(hEvents[EVENT_LOAD_ENDGAMEDB]);
	}

	void Engine::TerminateComputer()
	{
		::SetEvent(hEvents[EVENT_TERMINATE]);
		_taskThinking.wait();

		//_backgroundThreadFinished->WaitOne();
		CloseEvents();
	}

	void Engine::TaskLoadEndGameDB()
	{


	}

	void Engine::TaskThinking()
	{
		// create engine task
		auto taskEngine = create_task([]() -> void {
			EngineThread(NULL);
		});

		// reporting progress in Win 8 Edition possibly not needed
		// bEngineReportProgress = _bLoadingEndGameDB != true;
		bEngineReportProgress = false;
		if(bEngineReportProgress == true)
		{						
			BackgroundProcessInit(eBackgroundProcess::Thinking);
		}

		// run the engine
		::SetEvent(hEvents[EVENT_ENGINE_START]);				
		// wait for events

		while(true)
		{
			DWORD dwResult = ::WaitForMultipleObjectsEx(MAX_EVENTS, hEvents, FALSE, INFINITE, FALSE);
			if(dwResult >= WAIT_OBJECT_0 && dwResult < WAIT_OBJECT_0 + MAX_EVENTS)
			{
				// switch work
				dwResult -= WAIT_OBJECT_0;
				switch(dwResult)
				{
					// reporting progress in engine possibly not needed
					//case EVENT_REPORT_PROGRESS:
					//	{
					//		// get percents
					//		::WaitForSingleObject(hProgressMutex, INFINITE);
					//		int percents = iProgressPercents;
					//		::ReleaseMutex(hProgressMutex);
					//
					//		// restore event
					//		::ResetEvent(hEvents[EVENT_REPORT_PROGRESS]);
					//
					//		// do report
					//		ProgressReport(percents);
					//	} break;
				case EVENT_TERMINATE:
					::ResetEvent(hEvents[EVENT_TERMINATE]);
					// check to terminate loading endgame DB
					//if(_bLoadingEndGameDB == true)
					//{
					//	SetTerminationFlag();
					//	::WaitForSingleObject(hEvents[EVENT_LOADING_DB_EXIT], INFINITE);
					//	::ResetEvent(hEvents[EVENT_LOADING_DB_EXIT]);
					//	_bLoadingEndGameDB = false;
					//}

					// check to terminate engine thread
					::SetEvent(hEvents[EVENT_ENGINE_TERMINATE]);
					::WaitForSingleObjectEx(hEvents[EVENT_ENGINE_EXITED], INFINITE, FALSE);

					// finish
					goto Exit;				
				//case EVENT_COMPUTE_MOVE:
				//	::ResetEvent(hEvents[EVENT_COMPUTE_MOVE]);
				//	break;
				case EVENT_ENGINE_FINISHED:
					{
						::ResetEvent(hEvents[EVENT_ENGINE_FINISHED]);

						// report the end
						// possibly not needed
						//if(bEngineReportProgress == true)
						//{
						//	ProgressReport(100);					

						//	BackgroundProcessFinished();
						//}

						// probe the endgame database
						uint8 dtm = 0;
						if(bUseEndGameDB == true)
						{
							CzechMoveGen::ToggleMove(&bbStartPosition, &bbBestMove);
							//DbReader::ReaderDbLookup(&bbStartPosition, !bWhiteTurn, &dtm);
						}

						// terminate thread engine in Win8 Edition
						// check to terminate engine thread 
						::SetEvent(hEvents[EVENT_ENGINE_TERMINATE]);
						::WaitForSingleObjectEx(hEvents[EVENT_ENGINE_EXITED], INFINITE, FALSE);

						// create the output
						OutputBitBoard^ obb =  ConvertMoveBitBoard(bbBestMove);
						obb->Depth = iReachedDepth;
						obb->CalmPosDepth = iReachedCalmPosDepth;
						obb->NumberOfNodes = iNumberOfNodes;
						obb->NodesPerSecond = iNodesPerSec;
						obb->DTM = dtm;

						// report the move
						MoveComputed(obb);
						goto Exit;
					} break;
				} // switch
			}
		}

		Exit:;
	}



	//void Engine::BackgroundThreadWorker(eBackgroundProcess backgroundProcess)
	//{
	//	// do initialization
	//	_bLoadingEndGameDB = false;

	//	// start the engine thread
	//	::CreateThread(NULL, 0, EngineThread, NULL, 0, NULL);

	//	// do waiting for work
	//	while(true)
	//	{
	//		// wait for an event
	//		DWORD dwResult = ::WaitForMultipleObjects(MAX_EVENTS, hEvents, FALSE, INFINITE);

	//		// if some work
	//		if(dwResult >= WAIT_OBJECT_0 && dwResult < WAIT_OBJECT_0 + MAX_EVENTS)
	//		{
	//			// switch work
	//			dwResult -= WAIT_OBJECT_0;
	//			switch(dwResult)
	//			{
	//			case EVENT_REPORT_PROGRESS:
	//				{
	//					// get percents
	//					::WaitForSingleObject(hProgressMutex, INFINITE);
	//					int percents = iProgressPercents;
	//					::ReleaseMutex(hProgressMutex);
	//				
	//					// restore event
	//					::ResetEvent(hEvents[EVENT_REPORT_PROGRESS]);
	//				
	//					// do report
	//					ProgressReport(percents);
	//				} break;
	//			case EVENT_LOADING_DB_FINISHED:
	//				_bLoadingEndGameDB = false;
	//				::ResetEvent(hEvents[EVENT_LOADING_DB_FINISHED]);

	//				BackgroundProcessFinished();
	//				break;
	//			case EVENT_TERMINATE:
	//				::ResetEvent(hEvents[EVENT_TERMINATE]);
	//				// check to terminate loading endgame DB
	//				if(_bLoadingEndGameDB == true)
	//				{
	//					SetTerminationFlag();
	//					::WaitForSingleObject(hEvents[EVENT_LOADING_DB_EXIT], INFINITE);
	//					::ResetEvent(hEvents[EVENT_LOADING_DB_EXIT]);
	//					_bLoadingEndGameDB = false;
	//				}

	//				// check to terminate engine thread
	//				::SetEvent(hEvents[EVENT_ENGINE_TERMINATE]);
	//				::WaitForSingleObject(hEvents[EVENT_ENGINE_EXITED], INFINITE);

	//				// finish
	//				goto Exit;				
	//			case EVENT_LOADING_DB_EXIT:
	//				::ResetEvent(hEvents[EVENT_LOADING_DB_EXIT]);
	//				break;
	//			case EVENT_LOAD_ENDGAMEDB:
	//				{
	//					_bLoadingEndGameDB = true;

	//					// report full progress
	//					//ProgressReportEventArgs^ eProgressArgs = gcnew ProgressReportEventArgs();
	//					//eProgressArgs ->ProgressPercent = 100;
	//					//ProgressReport(this, eProgressArgs);					
	//					
	//					BackgroundProcessInit(eBackgroundProcess::PreloadingDatabase);

	//					// start loading thread
	//					::CreateThread(NULL, 0, BackgroundDBLoader, NULL, 0, NULL);

	//					// reset event
	//					::ResetEvent(hEvents[EVENT_LOAD_ENDGAMEDB]);
	//				} break;
	//			} // switch
	//		}
	//	} // while

	//Exit:;

	//	// tidy up
	//	_backgroundThreadFinished->Set();
	//}


	void Engine::ComputeMove(eCheckersType checkersType, const Array<Coordinate^>^ board, bool bWhite, int miliseconds, const Array<OutputBitBoard^>^ gameHistory)
	{
		// check if game started white player
		bool bFirstWhite = bWhite;
		if(gameHistory->Length % 2)
			bFirstWhite = ! bFirstWhite;

		BitBoard* bbGameHistory = NULL;
		BitBoard64* bbGameHistory64 = NULL;

		// convert position
		switch(checkersType)
		{
			case eCheckersType::CzechCheckers:
			case eCheckersType::EnglishCheckers:
				// set start position
				bbStartPosition = MakeBitBoard(board);
				CheckersType = checkersType == eCheckersType::EnglishCheckers ? EnglishCheckers : CzechCheckers;

				// save history of moves
				bbGameHistory = new BitBoard[gameHistory->Length];
				for(unsigned int i = 0; i < gameHistory->Length; i++)
					bbGameHistory[i] = ConvertFromOutputBitBoard(gameHistory[i]);
				PushMoveHistory(bbGameHistory, gameHistory->Length, bFirstWhite);

				break;
			case eCheckersType::InternationalCheckers:
				// set start position
				bbStartPosition64 = MakeBitBoard64(board);
				CheckersType = InternationalCheckers;

				// save history of moves
				bbGameHistory64 = new BitBoard64[gameHistory->Length];
				for(unsigned int i = 0; i < gameHistory->Length; i++)
					bbGameHistory64[i] = ConvertFromOutputBitBoard64(gameHistory[i]);
				PushMoveHistory(bbGameHistory64, gameHistory->Length, bFirstWhite);

				break;
			default:
				assert(false);
		}

		bWhiteTurn = bWhite;
		dwComputingTime = miliseconds;

		_taskThinking = create_task([this]
		{
			TaskThinking();			
		});
		

		//// signal the thread to compute the move
		//::SetEvent(hEvents[EVENT_COMPUTE_MOVE]);
	}
}
